iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
Software Development

Kotlin on the way系列 第 29

Day 29 設計模式 依賴注入的細節

  • 分享至 

  • xImage
  •  
  • 解決什麼問題
  • 如何理解
  • 職責
    • 控制反轉和依賴反轉
  • 抽象
  • 使用其他設計模式
  • 優點缺點
  • service locator

解決什麼問題

要依賴注入,就不能只懂依賴注入
而依賴注入是在做什麼呢?讓協作類別,依賴於基礎設施,來獲取所需的服務

依賴注入本身不是目標,而是手段,使用手段的目的是為了寫出更好維護的程式
在不斷增長的程式碼中,為了使開發更有效率,我們會盡力使其易於維護,而最好的方式就是鬆耦合,針對抽象寫程式,而不是實作,鬆耦合的設計能提高擴展性,高擴展性也會提高維護性,而所謂依賴注入,也不過是提高維護性的手法

di 是模式、原則、設計,但他不是 container or library,所謂 library 是定義了依賴注入方法的套件庫,讓開發者可以更輕鬆的實踐依賴注入,但他並非必要的,在不使用套件的情況下做的依賴注入稱之為簡單依賴注入

鬆耦合架構

鬆耦合的架構,可以讓程式更易於開發、維護、擴展、測試,而核心就是針對介面做設計,而不是實體
詳細的方法,在 Day 11 ~ Day 24 全都是可以用到的技巧

既然已經針對介面做出設計,也知道物件要依賴於抽象而非實體,那就剩下一個問題,拿如何獲得?
答案就是今天的主題 依賴注入

如何理解

在 stackoverflow 裡面有一串How to explain dependency injection to a 5-year-old? [closed]講到

When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might even be looking for something we don't even have or which has expired.

What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.

簡單的翻譯後就是,小朋友應該說出需求,而爸比或媽咪會去完成,像是你說你需要飲料配午餐,你就只需要坐著就好,爸比媽咪會確保你有飲料喝

職責

  1. 創建物件
  2. 知道哪些類別需要物件
  3. 提供物件

可以看到,透過依賴注入,我們可以將物件的建立,和物件的使用,兩種不同的責任分離,進一步減少物件的職責,也讓物件的內聚性更高

控制反轉和依賴反轉

inversion of control
依賴抽象,透過抽象去操作實際物件,是控制依賴關係的方向

dependency Inversion Principle
讓實作依賴於抽象,而不是抽象依賴於實作,是處理依賴關係的模式

抽象

抽象可以說是物件導向概念裡面最重要的一點,我會用餅乾模具來描述抽象

抽象就像是餅乾模具,而物件實體的定義就是我們壓出來的餅乾則是實體,抽象定義了小草的形狀,而實作的奶油餅乾、紅絲絨餅乾都會是小草的形狀,而當我們販售小草造型餅乾時,抹茶、奶油、紅絲絨三種餅乾都可以混賣

那回到這篇主題,為什麼抽象在這邊重要,我們不僅是讓物件依賴於實體、以介面去設計,定義依賴注入時才能讓我們替換不同的實體類別,當然以介面的觀點進行開發並不是為所有類別設計介面,那些不需要被模擬、介入攔截、替換的類別,就不需要將類別隱藏於介面之後

使用其他設計模式

依賴注入框架會定義類別建構方法,而在某些環境中,建構方法根據設計模式也會有所不同,導致開發者需要先理解設計模式、使用方法並瞭解依賴注入框架,舉個例子吧

// 要求使用建構者模式需要使用 provide
@Provide
fun provideRepository():Retrofit {
    return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService::class.java)
}

// 回傳介面時用 bind
@Binds
fun bindsRepository():Repository {
    
}

優點缺點

  • 晚期綁定
    • 在不需重新編譯的情況下,進行服務的替換
  • 擴展性
    • 重新利用程式碼做擴展
  • 平行開發
    • 開發工作可以同步進行
  • 可維護性
  • 可測試性

優點

  • 開發時間縮短
    • 短期內可能會增加,但以長期來看,使用依賴注入可以讓每個物件的職責更內聚,提高維護性
  • 耦合度降低

缺點

  • 學習成本
  • 複雜度增加
    • 當系統的物件都依賴於抽象介面時,追朔源碼會變得複雜,需要到抽象層,仰賴於開發者對程式碼跟ide操作的熟悉度
  • 耦合於依賴注入工具

service locator

服務定位器被稱為是反模式

Since Koin isn’t a dependency injector but a service locator with a clever reified trick that you can use to manually perform dependency injection, the boilerplate will scale disproportionally — said Jake Wharton.

服務定位與依賴注入最大的差異在於,他無法通過注入完全物件建構,我們必須手動傳入綁定

  single { Controller(get()) }

另一方面,服務定位器會在運行時動態建立物件實體,換句話說,他也會在運行時崩潰,而真正的依賴注入不同,他會產出程式碼,並在編譯時自動檢查依賴關係,如果沒有正確的提供依賴關係,我們便無法建構應用程序,就是說,他可以保證應運運行時不會有依賴關係的問題發生

reference

依賴注入:原理、實作與設計模式
Dependency Injection Principles, Practices, and Patterns 1st Edition

這本書我也是正在看,文章分享比起實作更重要的設計概念,如有錯誤歡迎指正

English

Day 29 design pattern detail on di

  • what issue it solves
  • hoe to understand
  • responsibility
    • dependency inversion and control inversion
  • abstract
  • using other design pattern
  • pros and cons
  • service locator

what issue it solves

Rather than other design pattern, di usually connect to other design pattern, so it would be better that you dive into di after you have basic knowledge of design pattern

So what does di doing? it allow the cooperator class, rely on basic instrument, to get what it needs

The di it self is not our goal, it is a solution, a solution for program which easier to maintain

In our growing program, we do everything we can to make it more maintainable, the best solution is decoupled, we write code based on abstract, not based on instance, the design of decouple could lead us to increase extendability, maintainability, the whole thing about di, is a solution to increase maintainability.

dependency inject is a pattern, principle, design, it is not just container or library, the library is a code base implement the idea of di, so we developer could use it simpler, but we of course could use di without it, a simple way if called simple dependency inject

decoupled architecture

The architecture of decoupled, allow the program easier to develop, maintain, extend, test, the core is rely on abstract, not instance
you can check out articles in this series, from day 11 to day 24

now we know we should design for abstract, and we also know that we rely on abstract not implement, so the last question is how do we get the constructor?
And that is our topic today, dependency inject

how to understand

There is a discussion in stackoverflow, How to explain dependency injection to a 5-year-old? [closed] mentioned

When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might even be looking for something we don't even have or which has expired.

What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.

responsibility

  1. create object
  2. knows which class need which object
  3. provide object

as we can see, by using di, we could separate the responsibility of object create and object usage, decrease the responsibility of object and increase the cohesions

dependency inversion and control inversion

inversion of control

  • rely on abstract, using abstract to operator instance, control the direction on dependency

dependency Inversion Principle

  • let instance rely on abstract, not reversed, a pattern to deal with dependency relationship

abstacrt

abstract is the more important point in oop, we will use cookie mold to describe it

abstract is like a cookie mold, the instance of class it the cookie dough we make, the abstract define the shape, and the instance butter cookie, red velvet cookie will the same shape of abstract, so when we selling grass shape cookie, we can mix the different cookie that are same shape

and back to the topic, why does abstract matter, we are not just make class rely on instance, design based on interface, when we define the dependency inject using abstract allow us to replace instance, on the other hands, not all class require an abstract, those class doesn't require mock, intercept, replace, doesn't require hiding class behind interface

using other design pattern

as we building instance with di, di would know the construct way of the class, and base on environment, we might have different usage depends on the class design, requires developer know about other design pattern, usage and di framework, we can take an example

// ask developer using `provide` with builder pattern
@Provide
fun provideRepository():Retrofit {
    return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService::class.java)
}

// using bind to return `interface`
@Binds
fun bindsRepository():Repository {
    
}

pros and cons

  • lately bindnig
    • replace service without recompile
  • extendability
    • reuse for extend
  • parallel
    • allow develop in same time
  • maintainability
  • testabilty

pros

  • shorter develop time
  • decoupled

cons

  • learn cost
  • increase complexity on debug
  • couple with di framework

service locator

some developer declare that service locator is an anti pattern

Since Koin isn’t a dependency injector but a service locator with a clever reified trick that you can use to manually perform dependency injection, the boilerplate will scale disproportionally — said Jake Wharton.

The biggest difference between service locator and di, it can't inject class automatically, we have to bind it manually

  single { Controller(get()) }

On the other hands, service locator will create class instance in runtime, in other words, it will crash in runtime as well, unlike to real di, di will auto generate code, and auto check dependency system, if we didn't provide right dependency relationship, we can't build our app, which means we guarantee we won;t have dependency in run time

reference

Dependency Injection Principles, Practices, and Patterns 1st Edition

Great book, this article mostly is concept from that book, but only contain a small piece


上一篇
Day 28 設計模式 狀態模式和狀態機
下一篇
Day 30 介面設計 Interface Design
系列文
Kotlin on the way31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言